Let x1 = y / z (division)
Let x2 = y // z (division, remainder)

divides one 16-bit value into another 16-bit value to compute a 16-bit result and remainder.

PBASIC allows you to divide any two variables--bits, bytes, words, or a mixture--to get a 16-bit result or remainder.

This routine duplicates that capability in assembly language. It uses a binary interpretation of the long-division method you were probably taught in school. It shifts the divisor left until there is a 1 in the msb. It then attempts to subtract this number from the dividend.

If the result of the subtraction is positive, the routine puts a 1 into the lsb of the quotient, shifts the quotient left, shifts the divisor right, and repeats the operation on what remains of the dividend. If the result of the subtraction is negative, the routine undoes the subtraction by adding the divisor and dividend back together, puts a 0 into the lsb of the quotient, shifts the quotient left, shifts the divisor right, and repeats the operation.

It may require a walk through the routine with paper and pencil (or the simulator) to grasp how this works. When the routine finishes, the 16-bit variable consisting of qH and qL holds the quotient or result of the division. The variable consisting of topH and topL holds the remainder.

Division by 0 is illegal, so the routine starts by checking for this. If division by 0 is attempted, the routine aborts and returns the error code 0FFh (255) in the w register. Otherwise, it returns with 0 in w. Your program should either prevent division by 0 or be prepared to act on the error code.

Demonstrating Divide.

To see Divide in operation, either run the program with the PSIM simulator, or connect the circuit below to an erasable PIC or PIC emulator, such as the Parallax downloader. Assemble and run DIVIDE.SRC. When you apply power to the PIC, the LEDs will light up in the binary pattern of the answer to the math problem 330Ah/00A3h, which is 50h with a remainder of 1Ah. So the quotient (50h) displays the following binary pattern on the LEDs:
 
 

0000 0000 0101 0000

Try dividing various values to see different results. Also, modify the program to display remainders (stored in the variables topH and topL) on the LEDs.


;
; ***************************************************************************
; ***  Bubble Software Parallax to PIC Source Converter. Copyright 1999.  ***
; ***  http://www.bubblesoftonline.com                 email: sales@picnpoke.com  ***
; ***************************************************************************
;
; DIVIDE
; Divide one 16-bit number into another, returning the 16-bit result and 
; the remainder. Upon entry, the top of the division fraction (the dividend)
; must be in topH and topL, and the bottom (divisor) in btmH and btmL. 
; If division by zero is attempted, the routine will return immediately with 
; the error code of 0FFh in w. Otherwise, it will perform the division, leaving 
; the remainder in topH and topL and the result (quotient) in qH and qL. 
; Upon return from a successful division, w contains 0. 

; Device data and reset vector
	P = pic16c55
	#include <16c55.inc>   ; processor assembler definitions
	_CONFIG _xt_osc & _wdt_off & _protect_off
        reset   start

        org     8
topH    Res      d'1'       ; MSB of top of fraction. 
topL    Res      d'1'       ; LSB of top of fraction. 
btmH    Res      d'1'       ; MSB of bottom of fraction.
btmL    Res      d'1'       ; LSB of bottom of fraction.
qH      Res      d'1'       ; MSB of quotient. 
qL      Res      d'1'       ; LSB of quotient. 
count   Res      d'1'       ; temporary counter
index   Res      d'1'       ; temporary counter

        org     0
start        MOVLW d'0'                 ; Set to outputs to display
             TRIS 6h
             MOVLW d'0'                 ; results on LEDs. 
             TRIS 7h
             MOVLW 0x33                 ; Problem: divide
             MOVWF topH
             MOVLW 0x0A                 ; 330Ah by 00A3h. 
             MOVWF topL
             MOVLW 0x00                 
             MOVWF btmH
             MOVLW 0x00A3               
             MOVWF btmL
             CALL divide                
             MOVF qL,w                  ; Display quotient in binary
             MOVWF 6h
             MOVF qH,w                  ; on LEDs connected to rb and rc. 
             MOVWF 7h
             GOTO $                     ; Endless loop

Divide       MOVF btmH,w                ; Check for division by 0.
             IORWF btmL,w               
             BTFSC status,z             
             RETLW d'255'               ; Error code= 255: return. 
             MOVLW d'1'                 ; Otherwise, initialize variables
             MOVWF count
             CLRF index                 ; for the division. 
             CLRF qH                    
             CLRF qL                    

Divide_sh_loop  BTFSC btmH,d'7'         ; Shift divisor left
             GOTO Divide_d1
             BCF status,c               ; until msb is in 
             RLF btmL                   ; btmH.7. 
             RLF btmH                   ; count = no. of shifts+1. 
             INCF count                 
             GOTO Divide_sh_loop        
Divide_d1    BCF status,c               
             RLF qL                     ; Shift quotient left.
             RLF qH                     
             MOVF btmL,w                ; top = top - btm. 
             SUBWF topL
             BTFSC status,c             ; If top - btm < 0 then 
             GOTO Divide_d2
             MOVLW d'1'                 ; top = top + btm
             SUBWF topH
             BTFSC status,c             ; The idea is to do the 
             GOTO Divide_d2
             INCF topH                  ; the subtraction and comparison
             MOVF btmL,w                ; (top > btm?) in one step. 
             ADDWF topL
             goto Divide_reentr		; Then, if btm > top, undo <Microchip instruction>
Divide_d2    MOVF btmH,w                ; the subtraction by adding
             SUBWF topH
             BTFSS status,c             ; top and btm back together
             goto Divide_less      	; <Microchip instruction>
             BSF qL,d'0'                
Divide_reentr
             BCF status,c               
             RRF btmH                   
             RRF btmL                   
             DECFSZ count               
             GOTO Divide_d1
             RETLW 0h                   ; Return w/ remainder in top
					; and result in q. 
Divide_less  MOVF btmL,w                ; btm > top, so 
             ADDWF topL
             BTFSC status,c             ; undo the subtraction by
             INCF topH                  ; adding them back together. 
             MOVF btmH,w                
             ADDWF topH
             goto    Divide_reentr    	; <Microchip instruction>

             
             
             end


; DIVIDE
; Divide one 16-bit number into another, returning the 16-bit result and 
; the remainder. Upon entry, the top of the division fraction (the dividend)
; must be in topH and topL, and the bottom (divisor) in btmH and btmL. 
; If division by zero is attempted, the routine will return immediately with 
; the error code of 0FFh in w. Otherwise, it will perform the division, leaving 
; the remainder in topH and topL and the result (quotient) in qH and qL. 
; Upon return from a successful division, w contains 0. 

        org     8
topH    ds      1       ; MSB of top of fraction. 
topL    ds      1       ; LSB of top of fraction. 
btmH    ds      1       ; MSB of bottom of fraction.
btmL    ds      1       ; LSB of bottom of fraction.
qH      ds      1       ; MSB of quotient. 
qL      ds      1       ; LSB of quotient. 
count   ds      1       ; temporary counter
index   ds      1       ; temporary counter

; Device data and reset vector
        device  pic16c55,xt_osc,wdt_off,protect_off
        reset   start
        org     0
start   mov     !rb,#0  ; Set to outputs to display
        mov     !rc,#0  ; results on LEDs. 
        mov     topH,#33h       ; Problem: divide
        mov     topL,#0Ah       ; 330Ah by 00A3h. 
        mov     btmH,#0h        
        mov     btmL,#0A3h
        call    divide
        mov     rb,qL   ; Display quotient in binary
        mov     rc,qH   ; on LEDs connected to rb and rc. 
        jmp     $       ; Endless loop

Divide  mov     w,btmH  ; Check for division by 0.
        OR      w,btmL
        snz
        retw    255     ; Error code= 255: return. 
        mov     count,#1        ; Otherwise, initialize variables
        clr     index   ; for the division. 
        clr     qH
        clr     qL

:sh_loop        jb      btmH.7,:d1      ; Shift divisor left
        clc             ; until msb is in 
        rl      btmL    ; btmH.7. 
        rl      btmH    ; count = no. of shifts+1. 
        inc     count   
        jmp     :sh_loop
:d1     clc
        rl      qL      ; Shift quotient left.
        rl      qH
        sub     topL,btmL       ; top = top - btm. 
        jc      :d2     ; If top - btm < 0 then 
        sub     topH,#1 ; top = top + btm
        jc      :d2     ; The idea is to do the 
        inc     topH    ; the subtraction and comparison
        add     topL,btmL       ; (top > btm?) in one step. 
        goto    :reentr ; Then, if btm > top, undo
:d2     sub     topH,btmH       ; the subtraction by adding
        sc              ; top and btm back together
        goto    :less
        setb    qL.0
:reentr clc
        rr      btmH
        rr      btmL
        djnz    count,:d1
        ret             ; Return w/ remainder in top
                        ; and result in q. 
:less   ADD     topL,btmL       ; btm > top, so 
        snc             ; undo the subtraction by
        inc     topH    ; adding them back together. 
        ADD     topH,btmH
        goto    :reentr

See also: